﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using gpx;

namespace GoldenTuney.GPX
{
    class GPXHelper
    {

        /// <summary>
        /// Takes a GPS file and creates an object from it
        /// </summary>
        /// <param name="filename">The filename of the GPS File</param>
        static public gpxType createGPX(FileStream file)
        {
            XmlSerializer ser = new XmlSerializer(typeof(gpxType));
            using (file)
            {
                gpxType info = (gpxType)ser.Deserialize(file);
                return info;
            }
        }

        /// <summary>
        /// flat earth approximation
        /// </summary>
        /// <param name="pt1"></param>
        /// <param name="pt2"></param>
        /// <param name="course"></param>
        /// <param name="dist"></param> 
        public static void GetCourseAndDistance(wptType pt1, wptType pt2, ref double course, ref double dist)
        {
            // convert latitude and longitude to radians
            double lat1 = DegreesToRadians((double)pt1.lat);
            double lon1 = DegreesToRadians((double)pt1.lon);
            double lat2 = DegreesToRadians((double)pt2.lat);
            double lon2 = DegreesToRadians((double)pt2.lon);

            // compute latitude and longitude differences
            double dlat = lat2 - lat1;
            double dlon = lon2 - lon1;

            double distanceNorth = dlat;
            double distanceEast = dlon * Math.Cos(lat1);

            // compute the distance in radians
            dist = Math.Sqrt(distanceNorth * distanceNorth + distanceEast * distanceEast);

            // and convert the radians to meters
            dist = RadiansToMeters(dist);

            // add the elevation difference to the calculation
            double dele = (double)pt2.ele - (double)pt1.ele;
            dist = Math.Sqrt(dist * dist + dele * dele);

            // compute the course
            course = Math.Atan2(distanceEast, distanceNorth) % (2 * Math.PI);
            course = RadiansToDegrees(course);
            if (course < 0)
                course += 360;
        }



        static public void OutputTracks(trkType[] tracks)
        {
            Console.WriteLine("{0} Tracks", tracks.Length);
            foreach (trkType track in tracks)
            {
                OutputTrack(track);
            }
        }

        public static string LatLonToString(double lat, double lon)
        {
            string latDir = (lat >= 0) ? "N" : "S";
            string lonDir = (lon >= 0) ? "E" : "W";
            double tLat = Math.Abs(lat);
            double tLon = Math.Abs(lon);
            double fLat = (tLat - Math.Truncate(tLat)) * 60;
            double fLon = (tLon - Math.Truncate(tLon)) * 60;
            string sLat = string.Format("{0}{1} {2}", latDir, Math.Truncate(tLat), fLat.ToString("#.000"));
            string sLon = string.Format("{0}{1} {2}", lonDir, Math.Truncate(tLon), fLon.ToString("#.000"));

            return string.Format("{0} {1}", sLat, sLon);
        }

        public static double DegreesToRadians(double degrees)
        {
            return degrees * Math.PI / 180.0;
        }

        public static double RadiansToDegrees(double radians)
        {
            return radians * 180.0 / Math.PI;
        }

        public static double RadiansToNauticalMiles(double radians)
        {
            // There are 60 nautical miles for each degree
            return radians * 60 * 180 / Math.PI;
        }

        public static double RadiansToMeters(double radians)
        {
            // there are 1852 meters in a nautical mile
            return 1852 * RadiansToNauticalMiles(radians);
        }

        // flat earth approximation
        public static void GetCourseAndDistance2(wptType pt1, wptType pt2,
          ref double course, ref double dist)
        {
            // convert latitude and longitude to radians
            double lat1 = DegreesToRadians((double)pt1.lat);
            double lon1 = DegreesToRadians((double)pt1.lon);
            double lat2 = DegreesToRadians((double)pt2.lat);
            double lon2 = DegreesToRadians((double)pt2.lon);

            // compute latitude and longitude differences
            double dlat = lat2 - lat1;
            double dlon = lon2 - lon1;

            double distanceNorth = dlat;
            double distanceEast = dlon * Math.Cos(lat1);

            // compute the distance in radians
            dist = Math.Sqrt(distanceNorth * distanceNorth + distanceEast * distanceEast);

            // and convert the radians to meters
            dist = RadiansToMeters(dist);

            // add the elevation difference to the calculation
            double dele = (double)pt2.ele - (double)pt1.ele;
            dist = Math.Sqrt(dist * dist + dele * dele);

            // compute the course
            course = Math.Atan2(distanceEast, distanceNorth) % (2 * Math.PI);
            course = RadiansToDegrees(course);
            if (course < 0)
                course += 360;
        }



        static void OutputTrack(trkType track)
        {
            Console.WriteLine("Track �{0}�", track.name);
            if (track.trkseg.Length > 0)
            {
                Console.WriteLine("{0} segments", track.trkseg.Length);
                foreach (trksegType seg in track.trkseg)
                {
                    Console.WriteLine("{0} Points", seg.trkpt.Length);
                    wptType wptPrev = seg.trkpt[0];
                    double totalDist = 0;
                    double elevationGain = 0;
                    foreach (wptType wpt in seg.trkpt)
                    {
                        double course = 0;
                        double dist = 0;
                        GetCourseAndDistance(wptPrev, wpt, ref course, ref dist);
                        totalDist += dist;
                        if (wpt.ele > wptPrev.ele)
                        {
                            elevationGain += (double)(wpt.ele - wptPrev.ele);
                        }
                        Console.WriteLine("{0} {1} {2} {3} {4} {5}", wpt.time, wpt.lat, wpt.lon, wpt.ele, course, dist);
                        wptPrev = wpt;
                    }
                    Console.WriteLine("Total distance = {0} kilometers. Elevation gain = {1} meters.",
                    totalDist / 1000, elevationGain);
                }
            }
        }
    }
}
